home *** CD-ROM | disk | FTP | other *** search
/ Chip 2000 June / Chip Haziran 2000.iso / prog / share / 35 / _SETUP.1 / Flic_Player.java < prev    next >
Encoding:
Java Source  |  1999-10-10  |  30.1 KB  |  924 lines

  1. /****************************************************************************************/
  2. /**  J.Anders     *   Germany                 *          TU-Chemnitz-Zwickau            */
  3. /****************************************************************************************/
  4. /** Fakultaet fuer Informatik                 *       Professur "Rechnernetze und       */
  5. /**                                           *          verteilte Systeme"             */
  6. /****************************************************************************************/
  7. /**   Telefon: +49 0371 531 1360              *         Fax: +49 371 531 1628           */
  8. /****************************************************************************************/
  9. /*                                                                                        */
  10. /* This file contains the whole source code for    a FLI/FLC file player in JAVA.            */
  11. /* Parameters:                                                                            */
  12. /*            FILENAME: URL of the FLI/FLC file (relative or absolut)                        */
  13. /*            DELAY (optional): delay (in ms) between 2 frames                            */
  14. /*                      during animation (default: 200)                                    */
  15. /*            INDIVDELAY (optional): "y" sets use of individual frame delay values        */
  16. /*                                    (default: don┤t use)                                */
  17. /*                        (see below)                                                        */
  18. /*                                                                                        */
  19. /* The FLI/FLC player works in 3 steps:                                                    */
  20. /*            1. Loading and parsing of the FLI/FLC file;                                    */
  21. /*            2. Reconstruction of the single frames;                                        */
  22. /*            3. Playing of the animation in a thread;                                    */
  23. /*                                                                                        */
  24. /****************************************************************************************/
  25. /* Individual frame delay capability has been added as follows:                            */
  26. /*    bytes 8 and 9 of each FLIC-frameheader now define (short) delay value for            */
  27. /*    this specific frame.                                                                */
  28. /*    If the applet┤s parameter INDIVDELAY is set to "y", these individual delay values    */ 
  29. /*    will be used!                                                                        */            
  30. /*        Jan Zimmermann    (jan@cosmigo.com; jzi@informatik.tu-chemnitz.de)                */
  31. /*        The animation software "Pro Motion" supports this special delay format and        */
  32. /*        is available at http://www.cosmigo.com/promotion                                */
  33. /*+++++++++++++++++++++++++++ library modules ++++++++++++++++++++++++++++++++++++++++++*/
  34.  
  35. import java.io.*;            
  36. import java.net.*;
  37. import java.applet.Applet;
  38. import java.awt.*;
  39. import java.awt.image.IndexColorModel;
  40. import java.awt.image.ColorModel;
  41. import java.awt.image.ImageProducer;
  42. import java.awt.image.ImageConsumer;
  43.  
  44. /*++++++++++++++++++++++++++ auxiliary classes containing data ++++++++++++++++++++++++++*/
  45.  
  46. class Err 
  47. {    // contains a possible error message;
  48.     public static String Msg = null;
  49. }
  50.  
  51. class Pixel_List
  52. {   // to build a list of frame data
  53.     public Pixel_List next = null;        // (one element per frame)
  54.     public byte color_values[];
  55.     public IndexColorModel curr_Model;    // the current color model
  56.     
  57.     Pixel_List (Pixel_List predecessor, IndexColorModel m, int width, int height) 
  58.         {
  59.          color_values = new byte[width * height];
  60.          if (m != null)
  61.             {     // new color model ?
  62.              curr_Model = m;    // adopt
  63.             }
  64.     
  65.          if (predecessor != null)
  66.             {
  67.              // If a predecessor frame is available its data are copied. That's because in general the
  68.              // FLI/FLC file contains a sequence of differences in respect of the predecessor frame.
  69.  
  70.              System.arraycopy(predecessor.color_values, 0, color_values, 0, color_values.length);
  71.  
  72.              // If no new color model is specified the color model of the predecessor frame is used:
  73.  
  74.              if (m == null) curr_Model = predecessor.curr_Model;
  75.              predecessor.next = this;
  76.             }
  77.         }
  78.         
  79. }
  80.  
  81. /*++++++++++++++++++++++++++++++++++++++ classes +++++++++++++++++++++++++++++++++++++++*/
  82.  
  83. /* The class "Flic_Player" implements the applet. It produces an object "scan" of the    */
  84. /* class "Scanner" and passes the FLC/FLI data to it. After that the variables:        */
  85. /*        scan.frame_counter                            */
  86. /*        scan.width                                */
  87. /*        scan.height                                */
  88. /* are set according to the FLI/FLI file data and the variable "scan.anchor" points to    */
  89. /* a list of frame data.                                */
  90.  
  91. public class Flic_Player extends Applet implements Runnable
  92. {
  93.     private int curr_nr = 0;    // current frame (during animation)
  94.     private Scanner scan;        // THE scanner
  95.        private Thread AnimatorThread;  // the animation runs as thread
  96.     
  97.     boolean painted = false;    // "true", if the frame "curr_nr" is painted
  98.     boolean loaded = false;    // "true", if the frame "curr_nr" is painted
  99.     private int delay = 200;    // delay between 2 frames (in ms)
  100.     private int use_indiv_delay=0; // "1" marks use of individual frame delays
  101.     private Image Images[];        // the sequence of images
  102.     private boolean io = true;    // "false" in case of errors
  103.     private Image LoadImage;
  104.  
  105.     public void init ()
  106.         {
  107.          // overwrites the method "init()" of Applet
  108.          String filename = null;    // THE Name of the FLI/FLC file
  109.          String s_delay = null;    // delay (as string)
  110.          String s_indivdelay = null ;    // String for param. INDIVDELAY
  111.          URL source_url;        // THE source (as URL)
  112.          Pixel_List pl;            // auxiliary variable to trace the list of images
  113.          DataInputStream in_stream = null; // THE input stream
  114.         
  115.          this.setBackground(new Color(128,128,128));
  116.  
  117.          try
  118.             { 
  119.              // obtain parameters
  120.              filename = getParameter("FILENAME");
  121.              s_delay = getParameter("DELAY");
  122.              s_indivdelay = getParameter("INDIVDELAY");
  123.              if (s_delay != null)
  124.                 {
  125.                  try
  126.                     {
  127.                      delay = Integer.parseInt(s_delay);
  128.                     } 
  129.                  catch (NumberFormatException e)
  130.                     { 
  131.                      Err.Msg = s_delay + " is not a number";
  132.                      io = false;
  133.                     }
  134.                 }
  135.  
  136.              if (s_indivdelay != null)
  137.                 if (s_indivdelay.toLowerCase().compareTo("y")==0)
  138.                     use_indiv_delay=1; // check if indiv. delays shall be used
  139.             }
  140.  
  141.         catch (NullPointerException e)
  142.             {
  143.              Err.Msg = "no parameter \"FILENAME\"";
  144.              io = false;
  145.             }
  146.  
  147.         if (filename == null)
  148.             {
  149.              Err.Msg = "no parameter \"FILENAME\"";
  150.              io = false;
  151.             }
  152.  
  153.         if (io) 
  154.             {
  155.              try 
  156.                 {    // construct URL and open the input stream:
  157.                  source_url = new URL(getCodeBase(), filename);
  158.                  in_stream = new DataInputStream(source_url.openStream());
  159.                 }
  160.              catch (MalformedURLException e)
  161.                 {
  162.                  Err.Msg = "MalformedURLException";
  163.                  io = false;
  164.                 }
  165.              catch (IOException e)
  166.                 {
  167.                  Err.Msg = "Cannot open: " + getCodeBase() + "/" + filename;
  168.                  io = false;
  169.                 }
  170.             }
  171.  
  172.         if (io)
  173.             {
  174.              scan = new Scanner(in_stream); // produce the scanner and pass
  175.              io = scan.scan_flic();          // the input stream to it
  176.              if (io)
  177.                 {
  178.                  resize(scan.width, scan.height); // but now possible because of
  179.                  Images = new Image[scan.frame_counter]; // "frame_counter"
  180.                  pl = scan.anchor;         // trace the list
  181.                  for (int i = 0; i < scan.frame_counter && io; i++)
  182.                     {
  183.                      if (pl == null)
  184.                         {
  185.                          // bad "frame_counter" ?
  186.                          Err.Msg = "error in tracing the list";
  187.                          io = false;
  188.                         }
  189.                      /*+++ produce one image per frame +++*/
  190.                      LoadImage= createImage( new myProducer(pl.color_values,
  191.                                              scan.width, scan.height, pl.curr_Model));
  192.                      Images[i] = this.createImage(scan.width,scan.height);
  193.                      Images[i].getGraphics().drawImage(LoadImage,0,0,this);
  194.                      LoadImage= null;
  195.                      if (use_indiv_delay==0)
  196.                         scan.delays[i]=delay; // if not indiv. frame delay capable then use global delay
  197.                      pl = pl.next;        // continue
  198.                     }
  199.                 }
  200.             }
  201.     
  202.      loaded=true;
  203.     }
  204.  
  205.     /*+++ The animation must be performed as thread because the   +++*/
  206.     /*+++ web browser expects that the "start()" method returns   +++*/
  207.     /*+++ after finite time. An infinite animation loop would     +++*/
  208.     /*+++ violate against this principle.                  +++*/
  209.  
  210.     public void run()
  211.         {
  212.          while(true)
  213.             if (loaded)
  214.                 {
  215.                     long starttime = System.currentTimeMillis();  // notice the start time
  216.                    painted = false;
  217.                  repaint();
  218.  
  219.                  try
  220.                     {
  221.                      // "sleep()" throws the "InterruptedException"
  222.                         starttime += scan.delays[curr_nr %  scan.frame_counter]; // compute the destination time
  223.                         
  224.                      // if destination time isn't reached --> sleep
  225.                         Thread.sleep(Math.max(0,starttime - System.currentTimeMillis()));
  226.                     }
  227.                  catch (InterruptedException e) { }
  228.             
  229.                  if (painted) curr_nr++ ;
  230.                 }
  231.                else
  232.                 try { Thread.sleep(500); }   catch (InterruptedException e)  { };
  233.         }
  234.  
  235.     /*+++ It is guaranteed that "start()" is called after "init()".    +++*/
  236.     /*+++ That means the image sequence list (anchor) already exists.  +++*/
  237.     /*+++ The "start()" method leaves behind only a runnable thread       +++*/
  238.     /*+++ and returns.                           +++*/
  239.  
  240.     public void start()
  241.         { 
  242.          // overwrites the "start()" method of the Applet
  243.          if (!io) return;
  244.             if (AnimatorThread == null)
  245.                 {
  246.                  // make sure there is no animation
  247.                     AnimatorThread = new Thread(this); // produce the thread
  248.                     AnimatorThread.start();      // pass the thread to the scheduler
  249.                 }
  250.         }
  251.  
  252.     /*+++ The "update()" method is called at the first opportunity        +++*/
  253.     /*+++ after "repaint()" has set the "Please repaint!" - Flag.        +++*/
  254.  
  255.        public void update(Graphics g)
  256.         {  
  257.          // overwrites the "update()" - method of Applet
  258.          paint(g);
  259.         }
  260.  
  261.     // "stop()" overwrites the "stop()" method of the Applet
  262.  
  263.     public void stop()
  264.         {
  265.          // is called if the page is unloaded
  266.          AnimatorThread = null; // IMPORTANT: otherwise the animation continues in background !!!
  267.         }
  268.  
  269.     /*+++ "paint()" is actually called after "update()" has cleared the      +++*/
  270.     /*+++ screen. Because "update()" is overwritten "paint()" is only called +++*/
  271.     /*+++ at the beginning and in case of exposure events. At this          +++*/
  272.     /*+++ opportunity a possible error message is displayed.         +++*/
  273.  
  274.     public void paint(Graphics g)
  275.         {
  276.          // overwrites the "paint()" method of the Applet
  277.          if (Err.Msg != null)
  278.             {
  279.                g.setFont(new Font(g.getFont().getName(), Font.BOLD, 10));
  280.              g.drawString(Err.Msg, 10, 100);
  281.              return;
  282.             }
  283.           g.drawImage(Images[curr_nr % scan.frame_counter], 0, 0, this);
  284.          painted=true;
  285.         }
  286. }
  287.  
  288. /*+++ The class "Scanner" provides the methods suitable to parse the FLI/FLC    +++*/
  289. /*+++ coded data. As a result of calling "scan_flic" the following (public)    +++*/
  290. /*+++ variables are set accordingly to the values in FLI/FLC file:        +++*/
  291. /*+++                                +++*/
  292. /*+++            frame_counter    number of frames        +++*/
  293. /*+++            width         width of one frame        +++*/
  294. /*+++            height         height of one frame    +++*/
  295. /*+++                                +++*/
  296. /*+++ Furthermore the variable "anchor" points to the beginning of a list of    +++*/
  297. /*+++ image data. Each image is stored as a sequence of pixel indexes in    +++*/
  298. /*+++ left-right/top-down manner. The indexes refer to an index color model     +++*/
  299. /*+++ which is stored in this list, too.                    +++*/
  300.  
  301. class Scanner
  302. {
  303.     /*+++ FLI/FLC constants:        +++*/
  304.  
  305.     final private int FLI_MAGIC = 0xAF11;
  306.     final private int HEADER_LENGTH = 26 + 102;
  307.     final private int FRAME_HEADER_LENGTH = 16;
  308.     final private short M_FLI = (short) 0xAF11;
  309.     final private short M_FLC = (short) 0xAF12;
  310.     final private short FLI_COLOR_256 = (short) 4;
  311.     final private short FLI_COLOR = (short) 11;
  312.     final private short FLI_LC    = (short) 12;
  313.     final private short FLI_WORD_LC = (short) 7;
  314.     final private short FLI_BLACK = (short) 13;
  315.     final private short FLI_BRUN  = (short) 15;
  316.     final private short FLI_COPY  = (short) 16;
  317.     final private short FRAME_ID  = (short) 0xf1fa;
  318.  
  319.     /*+++ public variables (will be set reasonably): +++*/
  320.  
  321.     public Pixel_List anchor = null;
  322.     public int width, height, frame_counter = 0;
  323.     public int delays[];
  324.     
  325.     /*+++ private variables: +++*/
  326.  
  327.     private boolean io = true;        // "false" if syntax error
  328.     private byte buffer[];            // portion of source data
  329.     private DataInputStream In_str;        // THE input stream
  330.     private Pixel_List last = null;        // end of the list of image data
  331.     private IndexColorModel myModel;    // "null" if no new color model specified 
  332.     private    byte r[] = new byte[256];    // sequence of red values in index color model
  333.     private    byte g[] = new byte[256];    // sequence of green values in index color model
  334.     private    byte b[] = new byte[256];    // sequence of blue values in index color model
  335.     private boolean frame_created = false; // indicates if memory for current frame has been created
  336.     
  337.     Scanner(DataInputStream f_In_str)
  338.         {
  339.          In_str = f_In_str;        // notice the input stream
  340.         }
  341.  
  342.     /* The method "scan_fli()" reads the content of the FLI/FLC-header.   */
  343.     /* It sets the variables "width" and "height".                  */
  344.     /* The return value is true if no parsing error occurs.              */
  345.  
  346.     public boolean scan_flic()
  347.         {
  348.          int length;    // length of file
  349.          short magic, size, speed;
  350.  
  351.          // read the whole header (fix length) to the"buffer":
  352.  
  353.          buffer = new byte[HEADER_LENGTH];
  354.          try
  355.             {
  356.              ReadContent(In_str, buffer, HEADER_LENGTH);
  357.             }
  358.          catch (IOException e)
  359.             {
  360.              Err.Msg = "read error 1";
  361.              return false;
  362.             }
  363.  
  364.          // extract content:
  365.  
  366.          length     =         to_int(buffer, 4,  0);
  367.          magic   = (short) to_int(buffer, 2,  4);
  368.          size    = (short) to_int(buffer, 2,  6); // specifies number of frames in flic
  369.          width   = (short) to_int(buffer, 2,  8);
  370.          height  = (short) to_int(buffer, 2, 10);
  371.          speed   = (short) to_int(buffer, 2, 16);
  372.  
  373.          delays = new int[size+1]; // create memory for individual frame delay value (+1 dummy for loop frame)
  374.  
  375.          switch (magic) 
  376.             { // OK ?
  377.              case M_FLI: break;
  378.              case M_FLC: break;
  379.              default: Err.Msg = "Number " + magic + "unknown";
  380.                  return false;
  381.             }
  382.  
  383.          length -= HEADER_LENGTH; // remaining length
  384.          // The frame count isn't specified in the header. That's why
  385.          // the end of file must be computed by means of file length.
  386.          // Such the frame count is computed implicitely.
  387.  
  388.          while ((length > 0) && io)
  389.             {
  390.              length -= scan_frame();
  391.              // The "frames_counter" cannot be incremented here
  392.              // because a frame not necessarily contains a an image
  393.              // but only a color model.
  394.             }
  395.         
  396.          frame_counter--; // erase the loop frame
  397.         
  398.          return io;
  399.         }
  400.  
  401.     /* The method "scan_frame()" reads data of one frame. A frame         */
  402.     /* contains a number of chunks. Such a chunk contains either        */
  403.     /* pixel data (FLI_BRUN), pixel difference data (FLI_LC in FLI or   */
  404.     /* FLI_WORD_LC in FLC) or a color map (FLI_COLOR in FLI or        */
  405.     /* FLI_COLOR_256 in FLC). FLI_COPY, FLI_BLACK and FLI_PREVIEW data  */
  406.     /* are ignored and I hope they will never occur.            */
  407.     /* The return value is the amount of data in bytes. The method         */
  408.     /* changes possibly the value of "io".                    */
  409.  
  410.     private int scan_frame()
  411.         {
  412.          int frame_size;
  413.          short fh_id, chunks;
  414.  
  415.          // read the whole frame header (fix length) to the"buffer":
  416.  
  417.          buffer = new byte[FRAME_HEADER_LENGTH];
  418.          try
  419.             {
  420.              ReadContent(In_str, buffer, FRAME_HEADER_LENGTH);
  421.             }
  422.          catch (IOException e)
  423.             {
  424.              Err.Msg = "read error 2";
  425.              io = false; return 0;
  426.             }
  427.  
  428.          // extract data:
  429.  
  430.          frame_size =      to_int(buffer, 4,  0);
  431.          fh_id   = (short) to_int(buffer, 2,  4); // tag
  432.          chunks  = (short) to_int(buffer, 2,  6); // count of chunks
  433.          delays[frame_counter] = (short) to_int(buffer, 2, 8);  // individual delay for this frame 
  434.  
  435.          // First the color model is set to "null". If an FLI_COLOR(_256)
  436.          // appears "myModel" is changed accordingly.
  437.  
  438.          myModel = null;
  439.          frame_created=false;
  440.  
  441.          if (fh_id != FRAME_ID)
  442.             { // check !
  443.              Err.Msg = "FLI/FLC synchronization error";
  444.              io = false; return 0;
  445.             } 
  446.  
  447.          for (int ch = 0; ch < chunks; ch++) 
  448.             {
  449.              // read the chunk data
  450.              scan_chunk();
  451.             }
  452.  
  453.          return frame_size;
  454.         }
  455.  
  456.     /* The method "scan_chunk()" reads one chunk. Depending on the chunk  */
  457.     /* type further methods are called.                      */
  458.  
  459.     private void scan_chunk()
  460.         {
  461.          int ch_size;
  462.          short ch_type;
  463.  
  464.          // read the chunk header (fix length) to the "buffer":
  465.  
  466.          buffer = new byte[6];
  467.          try 
  468.             {
  469.              ReadContent(In_str, buffer, 6);
  470.             }
  471.          catch (IOException e)
  472.             {
  473.              Err.Msg = "read error 3";
  474.              io = false; return;
  475.             }
  476.  
  477.          // extract data:
  478.  
  479.          ch_size =       to_int(buffer, 4, 0);
  480.          ch_type = (short) to_int(buffer, 2, 4);
  481.  
  482.          // read the remaining data to the "buffer":
  483.  
  484.          buffer = new byte[ch_size - 6];
  485.          try 
  486.             {    
  487.              ReadContent(In_str, buffer, ch_size - 6);
  488.             }
  489.          catch (IOException e) 
  490.             {
  491.              Err.Msg = "read error 4";
  492.              io = false; return;
  493.             }
  494.          switch (ch_type)
  495.             {
  496.              // dectect chunk type
  497.              case FLI_COLOR:        scan_FLI_COLOR(2);break;
  498.              case FLI_COLOR_256:    scan_FLI_COLOR(0);break;
  499.              case FLI_LC:            scan_FLI_LC();  break;
  500.              case FLI_WORD_LC:        scan_FLI_WORD_LC();  break;
  501.              case FLI_BRUN:            scan_FLI_BRUN(); break;
  502.              case FLI_BLACK:        break;
  503.              case FLI_COPY:            break;
  504.              case 0x12:                break; // skip postage stamp chunk
  505.              default: Err.Msg = "chunk type " + ch_type + " unknown";
  506.                 io = false; return;
  507.             }
  508.         }
  509.  
  510.     /* The method "scan_FLI_BRUN()" reads FLI_BRUN data. These data des-    */
  511.     /* cribe an image pixel by pixel. The data are possibly subdivided into */
  512.     /* packets. In the case of a sequence of equal pixel values only the    */
  513.     /* pixels value and the length of the sequence is given.        */
  514.     /* It is assumed that the FLI_BRUN data are given first. Otherwise the    */
  515.     /* player will fail.                            */
  516.  
  517.     private void scan_FLI_BRUN()
  518.         {
  519.          int idx = 0;    // offset in data buffer
  520.          int ppx;        // offset in frame
  521.          int pkts;        // count of packets in a line
  522.          int xchpx;        // current pixel value
  523.          int pixel;        // pixel counter
  524.          int line;        // current line
  525.          byte f;        // to remember a pixel value
  526.  
  527.          short line_count = (short) height;
  528.  
  529.          if (frame_counter > 0) 
  530.             {
  531.              // see above !
  532.              Err.Msg = "FLI_BRUN although frame_counter > 0"; io = false; return;
  533.             }
  534.  
  535.          if (myModel == null) 
  536.             {
  537.              // no color model ???
  538.              Err.Msg = "No color model for the first frame ?"; io = false; return;
  539.             }
  540.  
  541.          // produce the first element in the list of frames:
  542.  
  543.            last = anchor = new Pixel_List(null, myModel, width, height);
  544.           frame_counter++;
  545.          frame_created=true;
  546.  
  547.          idx = 0;
  548.             
  549.          for (line = 0; line < line_count; line++)
  550.             {
  551.              pkts = (0xff & buffer[idx++]);
  552.              ppx = line * width;
  553.              for (int pkt_nr = 0; pkt_nr < pkts; pkt_nr++) 
  554.                 {
  555.                  xchpx  = (int) buffer[idx++];
  556.                  if (xchpx >= 0)
  557.                     { // set the following byte "xchpx" times
  558.                      f = (byte) (0xff & buffer[idx++]);
  559.                      for (int k = 0; k < xchpx; k++) 
  560.                         {
  561.                          anchor.color_values[ppx++] = f;
  562.                         }
  563.                     }
  564.                    else
  565.                     { // the following "xchpx" bytes are pixel values
  566.                      xchpx = -xchpx;
  567.  
  568.                      for (pixel = 0; pixel < xchpx; pixel++)
  569.                         {
  570.                          anchor.color_values[ppx++ ] = (byte)(0xff & buffer[idx++]);
  571.                         }
  572.                     }
  573.                 }
  574.             }
  575.         }
  576.  
  577.  
  578.  
  579.     /* The method "scan_FLI_LC()" recognizes FLI_LC data which occur in FLI */
  580.     /* files. They describe the differences in respect of the predecessor    */
  581.     /* frame. After a possible skip of (unchanged) data at the beginning    */
  582.     /* follow the data of "line_count" lines. The data of every line are    */
  583.     /* possibly subdivided into packets with (possibly) some unchanged    */
  584.     /* pixels between them.                            */
  585.     /* It is assumed that FLI_LC data never occur first. Otherwise the     */
  586.     /* player will fail.                            */
  587.  
  588.     private void scan_FLI_LC() 
  589.         {
  590.          int idx = 0;    // offset in buffer
  591.          int ppx;    // offset in frame
  592.          int pkts;    // count of data packets in a line
  593.          int xchpx;    // current value
  594.          int pixel;    // pixel counter
  595.          int line;    // current line
  596.          short line_count; // count of lines to change (after a possbile skipping)
  597.          byte f;        // ro remember a pixel value
  598.  
  599.          idx = 0;
  600.          if (frame_counter == 0)
  601.             { // see above !
  602.              Err.Msg = "FLI_LC although frame_counter == 0"; io = false; return;
  603.             }
  604.  
  605.          // produce the next element in the list of frames:
  606.          // (The constructor copies the data of the predecessor
  607.          //  frame "last")
  608.  
  609. /*         last.next = new Pixel_List(last, myModel, width, height);
  610.          frame_counter++;
  611.            last = last.next;    // update */
  612.  
  613.          if (!frame_created)  // create new frame if it does not exist, except first frame
  614.             {
  615.              last.next = new Pixel_List(last, myModel, width, height);
  616.              frame_counter++;
  617.              last = last.next;    // update
  618.              frame_created=true;
  619.             } 
  620.  
  621.         line      =          to_int(buffer, 2, idx); idx += 2;
  622.         line_count = (short) to_int(buffer, 2, idx); idx += 2;
  623.             
  624.         for (; line < line_count; line++)
  625.             {
  626.              pkts = (0xff & buffer[idx++]);
  627.              ppx = line * width;
  628.              for (int pkt_nr = 0; pkt_nr < pkts; pkt_nr++)
  629.                 {
  630.                  ppx += (0xff & buffer[idx++]); // skip pixels
  631.                  xchpx = (int) buffer[idx++];
  632.                  if (xchpx < 0)
  633.                     { // set the following value "xchpx" times
  634.                      xchpx = -xchpx;
  635.                      f = (byte) (0xff & buffer[idx++]);
  636.                      for (int k = 0; k < xchpx; k++)
  637.                         {
  638.                          last.color_values[ppx++] = f;
  639.                         }
  640.                     }
  641.                    else
  642.                     {    // the following "xchpx" bytes are pixel values
  643.                      for (pixel = 0; pixel < xchpx; pixel++)
  644.                         {
  645.                          last.color_values[ppx++ ] =
  646.                             (byte)(0xff & buffer[idx++]);
  647.                         }
  648.                     }
  649.                 }
  650.             }
  651.         }
  652.     
  653.     /* The method "scan_FLI_WORD_LC()" recognizes FLI_WORD_LC data which    */
  654.     /* occur in FLC files. These are pixel difference values in respect of    */
  655.     /* the predecessor frame. The pixel values of "line_count" consecutive  */
  656.     /* lines are specified followed by a (possible) skip of (unchanged)     */
  657.     /* lines. The pixel values are given in double bytes. In the case of an    */
  658.     /* odd number of pixels per line the remaining pixel is given in a    */
  659.     /* special manner.                            */
  660.     /* It is assumed that FLI_WORD_LC data never occur first. Otherwise the    */
  661.     /* player will fail.                            */
  662.  
  663.     private void scan_FLI_WORD_LC()
  664.         {
  665.          int idx = 0;    // offset in buffer
  666.          int ppx;    // offset in frame
  667.          int xchpx;    // current value
  668.          int pixel;    // pixel counter
  669.          int line;    // line counter
  670.          int yoff;    // the real current line
  671.          short line_count; // count of lines to change
  672.          short pkts;    // count of packets per line
  673.          byte f1, f2;     // ro remember 2 pixel values
  674.          boolean last_pix_flag = false; // is a last single pixel stored ?
  675.          byte last_pixel = 0; // value of a last single pixel
  676.  
  677.          idx = 0;
  678.          if (frame_counter == 0)
  679.             { // see above!
  680.              Err.Msg = "FLI_LC_WORD although frame_counter == 0"; io = false; return;
  681.             }
  682.  
  683.          // produce the next element in the list of frames:
  684.          // (The constructor copies the data of the predecessor
  685.          //  frame "last")
  686.  
  687. /*         last.next = new Pixel_List(last, myModel, width, height);
  688.         frame_counter++;
  689.           last = last.next;    // update */
  690.         if (!frame_created)  // create new frame if it does not exist
  691.             {
  692.              last.next = new Pixel_List(last, myModel, width, height);
  693.              frame_counter++;
  694.              last = last.next;    // update
  695.              frame_created=true;
  696.             } 
  697.  
  698.         line_count = (short) to_int(buffer, 2, idx); idx += 2;
  699.         yoff = 0;
  700.         for (line = 0; line < line_count; line++)
  701.             {
  702.              pkts   =  (short) to_int(buffer, 2, idx); idx += 2;
  703.              while ((pkts & 0x8000) != 0) 
  704.                 {
  705.                  if ((pkts & 0x4000) != 0)
  706.                     { // no packet counter but ...
  707.                      yoff -=(int) pkts; // ...count of lines to skip
  708.                     }
  709.                    else
  710.                     {               // ...value of a last single pixel
  711.                      last_pix_flag = true; // notice!
  712.                      last_pixel = (byte) (pkts & 0xff);
  713.                     }
  714.                  // the packet count follows
  715.                  pkts   =  (short) to_int(buffer, 2, idx); idx += 2;
  716.                 }
  717.  
  718.              ppx = yoff * width;
  719.              for (int pkt_nr = 0; pkt_nr < pkts; pkt_nr++) 
  720.                 {
  721.                  ppx +=  (0xff & buffer[idx++]); // skip pixels
  722.                  xchpx = (int) buffer[idx++];
  723.                  if (xchpx < 0)
  724.                     { // set the following 2 bytes "xchpx" times
  725.                      xchpx = -xchpx;
  726.                      f1 = (byte) (0xff & buffer[idx++]);
  727.                      f2 = (byte) (0xff & buffer[idx++]);
  728.                      for (int k = 0; k < xchpx; k++)
  729.                         {
  730.                          last.color_values[ppx++] = f1;
  731.                          last.color_values[ppx++] = f2;
  732.                         }
  733.                     }
  734.                    else
  735.                     {    // "xchpx" double bytes follow
  736.                      for (pixel = 0; pixel < xchpx; pixel++)
  737.                         {
  738.                          last.color_values[ppx++ ] =
  739.                             (byte)(0xff & buffer[idx++]);
  740.                          last.color_values[ppx++ ] =
  741.                             (byte)(0xff & buffer[idx++]);
  742.                         }
  743.                     }
  744.                 }
  745.  
  746.              if (last_pix_flag)
  747.                 { // is there a value of a last single pixel ?
  748.                  last.color_values[(yoff + 1) * width - 1] = last_pixel;
  749.                  last_pix_flag = false;
  750.                 }
  751.  
  752.              yoff++; // count the lines
  753.             }
  754.         }
  755.  
  756.     /* The method "scan_FLI_COLOR(int shift)" recognizes FLI_COLOR data as  */
  757.     /* well as FLI_COLOR_256 data. The latter occur in FLC files, the other */
  758.     /* in FLI files. The data describe a color map with 256 color values    */
  759.     /* maximal. The difference is: The RGB values in FLI_COLOR_256 are 8     */
  760.     /* bit values; the RGB values in FLI_COLOR are 6 bit values. That means */
  761.     /* the FLI_COLOR data must be "shift"-ed 2 bytes to the left. The    */
  762.     /* FLI_COLOR(_256) data are (eventually) subdivided into packets and are*/
  763.     /* (possibly) differences in respect of the predecessor color map.     */
  764.     /* Therefore a skipping is possible. Since the arrays "r", "g" and "b"  */
  765.     /* are private global variables it is guaranteed that the new values    */
  766.     /* are always built in recpect to the existing one.            */
  767.     /* The method produces a new color model "myModel".            */
  768.  
  769.     private void scan_FLI_COLOR(int shift)
  770.         {
  771.          short pkts;        // count of packets
  772.          int skip;        // count of values to skip
  773.          int count;        // count of values in a packet
  774.          int idx = 0;        // offset in buffer
  775.          int table_idx = 0;    // index in color map
  776.          pkts = (short) to_int(buffer, 2, 0); idx += 2;
  777.          for (int pkt_nr = 0; pkt_nr < pkts; pkt_nr++)
  778.             {
  779.              skip = 0xff & (buffer[idx++]);
  780.              count= 0xff & (buffer[idx++]);
  781.              table_idx += skip;
  782.              if (count == 0) count = 256;    // zero means 256 !!!
  783.              for (int j = 0; j < count; j++)
  784.                 { // read RGB values
  785.                  r[table_idx  ] = (byte) ((0xff & buffer[idx++]) << shift);
  786.                  g[table_idx  ] = (byte) ((0xff & buffer[idx++]) << shift);
  787.                  b[table_idx++] = (byte) ((0xff & buffer[idx++]) << shift);
  788.                 }
  789.             }
  790.  
  791.         // After reading the RGB values a new index color model is produced:
  792.  
  793.          myModel = new IndexColorModel(8, 256, r, g, b);
  794.          if ((!frame_created)&&(anchor!=null))  // create new frame if it does not exist, except first frame
  795.             {
  796.              last.next = new Pixel_List(last, myModel, width, height);
  797.              last = last.next;    // update
  798.              frame_counter++;
  799.              frame_created=true;
  800.             } 
  801.         }
  802.  
  803.  
  804.     /* The method "ReadContent (DataInputStream Stream, byte field[], int size)"  */
  805.     /* reads from "Stream" into the "field" until "size" bytes are read or a read */
  806.     /* error occurs.                                  */
  807.  
  808.     private void ReadContent (DataInputStream Stream, byte field[], int size)
  809.         throws IOException 
  810.         {
  811.          int bytes_read = 0;
  812.          while (bytes_read < size)
  813.             {
  814.              bytes_read += Stream.read(field, bytes_read, size - bytes_read);
  815.             }
  816.         }
  817.  
  818.     /* Because JAVA has no pointers and no unsigned types a special method        */
  819.     /*           "int to_int(byte b[], int length, int off)"            */
  820.     /* is necessary which reads "length" Bytes at position "off" from "b" and   */
  821.     /* translates it to a "length" byte integer value.                */
  822.  
  823.     private int to_int(byte b[], int length, int off)
  824.         {
  825.          int r_val = 0;
  826.          for (int i = 0; i < length; i++)
  827.             {
  828.              r_val <<= 8;
  829.              r_val |= b[length - 1 - i + off] & 0xff;
  830.             }
  831.          return r_val;
  832.         }
  833.     }
  834.  
  835.  
  836. /*+++ JAVA offers 2 possibilities to create an image:                +++*/
  837. /*+++                                        +++*/
  838. /*+++            1. createImage(int width, int height);            +++*/
  839. /*+++            2. createImage(ImageProducer producer);            +++*/        
  840. /*+++                                        +++*/
  841. /*+++ The first method isn't suitable here because it requires to draw the    +++*/
  842. /*+++ pixel values by means of a sequence of "draw...()" methods into the image.+++*/
  843. /*+++ Since all the pixel values and the index color model are known the images +++*/
  844. /*+++ are produced according to the second method by means of objects of the    +++*/
  845. /*+++ class "myProducer" which implements the interface "ImageProducer".    +++*/
  846. /*+++ An image producer is an object which supplies the pixel values on demand  +++*/
  847. /*+++ of an image consumer. The pixel values are delivered in recpect of a     +++*/
  848. /*+++ certain color model which must be known to the image consumer, too.    +++*/
  849. /*+++ Therefore the image producer must also supply the color model on demand.  +++*/
  850.  
  851. class myProducer implements ImageProducer
  852.     {
  853.      private byte Pix_Data[];    // THE pixel values
  854.      private int width, height;    // THE dimensionens
  855.      ColorModel  Model;        // THE color model
  856.  
  857.     // The constructor only assures that the image producer
  858.     // notices the pixel values, the dimension and the
  859.     // color model.
  860.  
  861.      myProducer(byte pixels[], int w, int h, ColorModel cm) 
  862.         {
  863.          Pix_Data = pixels; // Notice that this is a kind of 
  864.                    // "pointer assignement"! Is is
  865.                    // possible because the constructor of
  866.                    // "Pixel_List" copies the pixel values.
  867.          width = w; height = h; Model = cm;
  868.         }
  869.  
  870.     // Before the first use the image consumer registers itself
  871.     // to the image producer.
  872.     // At this opportunity he is informed of the dimension and
  873.     // the color model.
  874.  
  875.     public void addConsumer(ImageConsumer  ic)
  876.         {
  877.          ic.setDimensions(width, height);
  878.          ic.setColorModel(Model);
  879.         }
  880.  
  881.     public boolean isConsumer(ImageConsumer  ic)
  882.         {
  883.          return true; // dummy
  884.         }
  885.  
  886.     public void removeConsumer(ImageConsumer  ic)
  887.         {
  888.         // dummy
  889.         }
  890.  
  891.     // The following 2 methods are almost identically.
  892.     // They are called by the image consumer to get the
  893.     // pixel data:
  894.  
  895.     public void requestTopDownLeftRightResend(ImageConsumer  ic)
  896.         {
  897.          // Tell the image consumer that the pixel data
  898.          // will be supplied from left to right und from
  899.          // top to bottom:
  900.  
  901.          ic.setHints(ImageConsumer.TOPDOWNLEFTRIGHT);
  902.  
  903.          // Send all the pixel data:
  904.  
  905.          ic.setPixels(0, 0, width, height, Model, Pix_Data, 0, width);
  906.  
  907.          // Tell the image consumer that no further data follow:
  908.  
  909.          ic.imageComplete(ImageConsumer.STATICIMAGEDONE);
  910.         }
  911.  
  912.     // The method "startProduction(ImageConsumer ic)" is called by
  913.     // the image consumer if it produces the image.
  914.     // For security it is informed of the color model
  915.     // and the dimensions (I don't know if this is really nenecessary).
  916.  
  917.     public void startProduction(ImageConsumer ic) 
  918.         {
  919.          ic.setDimensions(width, height);
  920.          ic.setColorModel(Model);
  921.          requestTopDownLeftRightResend(ic);
  922.         }
  923.     }
  924.